package org.mcflier.appuntamento.io.driver.rss.torrent;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.Hashtable;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Logger;

import org.mcflier.appuntamento.core.Factory;
import org.mcflier.appuntamento.core.exception.ModulesException;
import org.mcflier.appuntamento.core.exception.PropertiesException;
import org.mcflier.appuntamento.core.logger.LoggerManager;
import org.mcflier.appuntamento.core.modules.IModule;
import org.mcflier.appuntamento.core.modules.ModuleNode;
import org.mcflier.appuntamento.core.properties.PropertiesManager;
import org.mcflier.appuntamento.data.model.TorrentSource;
import org.mcflier.appuntamento.io.driver.rss.RssTools;
import org.mcflier.appuntamento.io.exception.DomainRemoteResourceException;

public class TorrentAggregator implements IModule{
	
	/*private static TorrentAggregator instance;
	
	static{
		instance = null;
	}
	
	public static TorrentAggregator getInstance() throws PropertiesException, InstantiationException, IllegalAccessException, ClassNotFoundException{
		if(instance == null)
			instance = new TorrentAggregator();
		
		return instance;
	}*/
	
	private static final String MODULE_SOURCES_TAG = "sources";
	private static final String MODULE_SOURCE_TAG = "source";
	private static final String MODULE_SOURCE_ID_TAG = "id";
	private static final String MODULE_SOURCE_NAME_TAG = "name";
	private static final String MODULE_SOURCE_DOMAING_TAG = "domain";
	private static final String MODULE_SOURCE_PATH_TAG = "path";
	private static final String MODULE_RSSLINKS_TAG = "RSSLinks";
	private static final String MODULE_RSSLINK_TAG = "RSSLink";
	private static final String MODULE_RSSLINK_TYPE_TAG = "type";
	private static final String MODULE_RSSLINK_URL_TAG = "url";
	
	private Logger logger;
	private Properties properties;
	private Map<String, AbstractRssTorrentDriver> drivers;
	
	public TorrentAggregator() throws PropertiesException, ModulesException {
		this.logger = ((LoggerManager)Factory.getInstance().getModuleInstance(Factory.MODULE_LOGGER)).getLogger(RssTools.class.getName());
		
		PropertiesManager propertiesManager = (PropertiesManager)Factory.getInstance().getModuleInstance(Factory.MODULE_PROPERTIES);
		this.properties = propertiesManager.getProperties();
		this.drivers = new Hashtable<String, AbstractRssTorrentDriver>();
	}
	
	private void checkLink(String domain, String link) throws DomainRemoteResourceException, MalformedURLException{
		try {
			URL url = new URL(link);
			if(!url.getHost().toLowerCase().equals(domain.toLowerCase()))
				throw new DomainRemoteResourceException("This resource ("+link+") doesn't correspond with this domain ("+domain+")");
		} catch (MalformedURLException e) {
			this.logger.warning("Adding resource link is not valid: "+link);
			throw e;
		}
	}
	
	public AbstractRssTorrentDriver getDriver(String name){
		return this.drivers.get(name);
	}
	
	public Map<String, AbstractRssTorrentDriver> getDrivers(){
		return this.drivers;
	}

	@Override
	public void receiveModuleContent(ModuleNode moduleContent) {
		this.logger.info("TorrentAggregator recieving the module content");
		
		this.drivers.clear();
		ModuleNode sources = moduleContent.getNode(MODULE_SOURCES_TAG);
		if(sources.getNodes().size()==0){
			ModuleNode temp = new ModuleNode("source");
			temp.add(MODULE_SOURCE_ID_TAG, sources.getAttribute(MODULE_SOURCE_ID_TAG));
			temp.add(MODULE_SOURCE_NAME_TAG, sources.getAttribute(MODULE_SOURCE_NAME_TAG));
			temp.add(MODULE_SOURCE_PATH_TAG, sources.getAttribute(MODULE_SOURCE_PATH_TAG));
			
			sources.getNodes().clear();
			sources.add(temp.getName(), temp);
		}
		
		if(sources.getNodes().size()==1 && sources.getNode(MODULE_SOURCE_TAG)==null){
			ModuleNode source = new ModuleNode("source");
			Set<String> keys = sources.getAttributes().keySet();
			for (String key : keys) {
				source.add(key, sources.getAttribute(key));
			}
			keys = sources.getNodes().keySet();
			for (String key : keys) {
				source.add(key, sources.getNode(key));
			}
			sources.getNodes().clear();
			sources.add(source.getName(), source);
		}
		
		Set<String> keys = sources.getNodes().keySet();
		TorrentSource source;
		ModuleNode nodeSource;
		for (String key : keys) {
			nodeSource = sources.getNode(key);
			String urlDriver = nodeSource.getAttribute(MODULE_SOURCE_PATH_TAG);
			if(urlDriver != null){
				try {
					source = new TorrentSource(
							Integer.valueOf(nodeSource.getAttribute(MODULE_SOURCE_ID_TAG)),
							nodeSource.getAttribute(MODULE_SOURCE_NAME_TAG),
							nodeSource.getAttribute(MODULE_SOURCE_DOMAING_TAG));

					ModuleNode links = nodeSource.getNode(MODULE_RSSLINKS_TAG);
					ModuleNode link;
					if(links.getNodes().size() == 0){
						link = new ModuleNode(MODULE_RSSLINK_TAG);
						link.add(MODULE_RSSLINK_TYPE_TAG, links.getAttribute(MODULE_RSSLINK_TYPE_TAG));
						link.add(MODULE_RSSLINK_URL_TAG, links.getAttribute(MODULE_RSSLINK_URL_TAG));

						links.add(MODULE_RSSLINK_TAG, link);
					}

					Set<String> linkKeys = links.getNodes().keySet();
					String type, url;
					for (String linkKey : linkKeys) {
						link = links.getNode(linkKey);
						type = link.getAttribute(MODULE_RSSLINK_TYPE_TAG);
						url = link.getAttribute(MODULE_RSSLINK_URL_TAG);
						try {
							this.checkLink(source.getDomain(), url);
							if(url!=null)
								if(type.trim().toLowerCase().equals("hd"))
									source.addHDSource(url);
								else if(type.trim().toLowerCase().equals("sd"))
									source.addSDSource(url);
								else //BOTH
									source.addNotClassifSource(url);
						} catch (MalformedURLException e) {
							this.logger.warning("The rss link '"+url+"' is not a url");
						} catch (DomainRemoteResourceException e) {
							this.logger.warning("The rss link '"+url+"' has not the right domain (right domain: "+source.getDomain()+"')");
						}
					}
					
					AbstractRssTorrentDriver driver = (AbstractRssTorrentDriver)Class.forName(urlDriver).newInstance();
					driver.init(this.properties, source);
					this.drivers.put(driver.getSource().getName(), driver);
					this.logger.info("Driver '"+urlDriver+"' readed from modules file and created");
				} catch (NumberFormatException e) {
					this.logger.warning("Error getting the id from the rss source '"+nodeSource.getAttribute(MODULE_SOURCE_NAME_TAG)+"'. (Id: "+nodeSource.getAttribute(MODULE_SOURCE_ID_TAG)+")");
				} catch (NullPointerException e) {
					this.logger.warning(e.getMessage());
				} catch (InstantiationException e) {
					this.logger.warning("Error creating the driver '"+urlDriver+"'. This class cannot be instantiated (abstract, interface...)");
				} catch (IllegalAccessException e) {
					this.logger.warning("Error creating the driver '"+urlDriver+"'. This class has not a public and empty constructor");
				} catch (ClassNotFoundException e) {
					this.logger.warning("Error creating the driver '"+urlDriver+"'. This class is missing");
				}
			}
			else
				this.logger.warning("Error obtaining the driver path for source '"+nodeSource.getAttribute(MODULE_SOURCE_NAME_TAG)+"'");
		}
	}
}